﻿using Windows.UI.Xaml;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using Device = SharpDX.Direct3D11.Device;
using FeatureLevel = SharpDX.Direct3D.FeatureLevel;
using SharpDX.DirectWrite;
using SharpDX.WIC;
using System;
using Windows.Foundation;

namespace Yinyue200.Sharp2D.Drawing
{
    
    public class ImageMaker: System.IDisposable
    {
        public interface IImageSource: System.IDisposable
        {
            ImageMaker Parent { get; set; }
            void BeginDraw();
            //void BeginDraw(Windows.Foundation.Rect updateRect);
            void EndDraw();
            Windows.UI.Xaml.Media.ImageSource Source { get; }
            RenderTarget RenderTarget { get; }
            void SetedParent();
        }
        public class DxgiImageSource : Windows.UI.Xaml.Media.Imaging.SurfaceImageSource, IImageSource
        {
            public void SetedParent()
            {

            }
            //private Device d3dDevice;
            private Device d3dDevice;
            private SharpDX.Direct2D1.DeviceContext d2dContext;
            public ImageMaker Parent { get; set; }

            public Windows.UI.Xaml.Media.ImageSource Source
            {
                get
                {
                    return this;
                }
            }

            public RenderTarget RenderTarget
            {
                get
                {
                    return d2dContext;
                }
            }

            //private Device d3dDevice;
            public DxgiImageSource(int pixelWidth, int pixelHeight, bool isOpaque)
                : base(pixelWidth, pixelHeight, isOpaque)
            {
                CreateDeviceResources();
                Windows.UI.Xaml.Application.Current.Suspending += Current_Suspending;
                //System.Diagnostics.Debug.WriteLine("enable:tbbxgi" + GetHashCode());

            }

            private void Current_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
            {
                Trim();
            }


            // Initialize hardware-dependent resources.
            void CreateDeviceResources()
            {
                Utilities.Dispose(ref d3dDevice);
                Utilities.Dispose(ref d2dContext);

                // This flag adds support for surfaces with a different color channel ordering
                // than the API default. It is required for compatibility with Direct2D.
                var creationFlags = DeviceCreationFlags.BgraSupport;

#if DEBUG
                // If the project is in a debug build, enable debugging via SDK Layers.
                //creationFlags |= DeviceCreationFlags.Debug;
#endif

                // This array defines the set of DirectX hardware feature levels this app will support.
                // Note the ordering should be preserved.
                // Don't forget to declare your application's minimum required feature level in its
                // description.  All applications are assumed to support 9.1 unless otherwise stated.
                FeatureLevel[] featureLevels =
                {
                FeatureLevel.Level_11_1,
                FeatureLevel.Level_11_0,
                FeatureLevel.Level_10_1,
                FeatureLevel.Level_10_0,
                FeatureLevel.Level_9_3,
                FeatureLevel.Level_9_2,
                FeatureLevel.Level_9_1,
            };
                // Create the Direct3D 11 API device object.
                d3dDevice = new Device(DriverType.Hardware, creationFlags, featureLevels);

                // Get the Direct3D 11.1 API device.
                using (var dxgiDevice = d3dDevice.QueryInterface<SharpDX.DXGI.Device>())
                {
                    // Create the Direct2D device object and a corresponding context.
                    using (var d2dDevice = new SharpDX.Direct2D1.Device(dxgiDevice))
                    {
                        d2dContext = new SharpDX.Direct2D1.DeviceContext(d2dDevice, DeviceContextOptions.None);
                    }
                    using (var sisNative = ComObject.QueryInterface<ISurfaceImageSourceNative>(this))
                        sisNative.Device = dxgiDevice;
                }
            }
            public void BeginDraw()
            {
                BeginDraw(new Windows.Foundation.Rect(0, 0,  Parent.Width, Parent.Height));
            }
            /// <summary>
            /// If the app window that contains the SurfaceImageSource isn't active, like when it's suspended, calling the BeginDraw method returns an error.
            /// </summary>
            /// <param name="updateRect"></param>
            public void BeginDraw(Rect updateRect)
            {
                // Express target area as a native RECT type.
                var updateRectNative = new Rectangle
                {
                    Left = (int)updateRect.Left,
                    Top = (int)updateRect.Top,
                    Right = (int)updateRect.Right,
                    Bottom = (int)updateRect.Bottom
                };

                // Query for ISurfaceImageSourceNative interface.
                using (var sisNative = ComObject.QueryInterface<ISurfaceImageSourceNative>(this))
                {
                    // Begin drawing - returns a target surface and an offset to use as the top left origin when drawing.
                    try
                    {
                        RawPoint offset;
                        using (var surface = sisNative.BeginDraw(updateRectNative, out offset))
                        {

                            // Create render target.
                            using (var bitmap = new SharpDX.Direct2D1.Bitmap1(d2dContext, surface))
                            {
                                // Set context's render target.
                                d2dContext.Target = bitmap;
                            }

                            // Begin drawing using D2D context.
                            d2dContext.BeginDraw();

                            // Apply a clip and transform to constrain updates to the target update area.
                            // This is required to ensure coordinates within the target surface remain
                            // consistent by taking into account the offset returned by BeginDraw, and
                            // can also improve performance by optimizing the area that is drawn by D2D.
                            // Apps should always account for the offset output parameter returned by 
                            // BeginDraw, since it may not match the passed updateRect input parameter's location.
                            //d2dContext.PushAxisAlignedClip(
                            //    new RectangleF(
                            //        (offset.X),
                            //        (offset.Y),
                            //        (offset.X + (float)updateRect.Width),
                            //        (offset.Y + (float)updateRect.Height)
                            //        ),
                            //    AntialiasMode.Aliased
                            //    );

                            //d2dContext.Transform = Matrix3x2.Translation(offset.X, offset.Y);
                        }
                    }
                    catch (SharpDXException ex)
                    {
                        if (ex.ResultCode == SharpDX.DXGI.ResultCode.DeviceRemoved ||
                            ex.ResultCode == SharpDX.DXGI.ResultCode.DeviceReset)
                        {
                            // If the device has been removed or reset, attempt to recreate it and continue drawing.
                            CreateDeviceResources();
                            BeginDraw(updateRect);
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }

            public void EndDraw()
            {
                // Remove the transform and clip applied in BeginDraw since
                // the target area can change on every update.
                //d2dContext.Transform = Matrix3x2.Identity;
                //d2dContext.PopAxisAlignedClip();

                // Remove the render target and end drawing.
                d2dContext.EndDraw();
                d2dContext.Target = null;

                // Query for ISurfaceImageSourceNative interface.
                using (var sisNative = ComObject.QueryInterface<ISurfaceImageSourceNative>(this))
                    sisNative.EndDraw();
            }

            public void Trim()
            {
                // Hints to the driver that the app is entering an idle state and that its memory can be used temporarily for other apps.
                using (var dxgiDevice = d3dDevice?.QueryInterfaceOrNull<SharpDX.DXGI.Device3>())
                    dxgiDevice?.Trim();
            }


            #region IDisposable Support
            private bool disposedValue = false; // 要检测冗余调用

            protected virtual void Dispose(bool disposing)
            {
                if (!disposedValue)
                {
                    if (disposing)
                    {
                        // TODO: 释放托管状态(托管对象)。

                    }
                    Windows.UI.Xaml.Application.Current.Suspending -= Current_Suspending;
                    //Trim();
                    //SharpDX.Utilities.FreeMemory(d3dDevice.NativePointer);
                    using (var sisNative = ComObject.QueryInterface<ISurfaceImageSourceNative>(this))
                        sisNative.Device = null;
                    Utilities.Dispose(ref d3dDevice);
                    Utilities.Dispose(ref d2dContext);

                    // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                    // TODO: 将大型字段设置为 null。

                    disposedValue = true;
                }
            }

            // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
            ~DxgiImageSource()
            {
                // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
                Dispose(false);
            }

            // 添加此代码以正确实现可处置模式。
            public void Dispose()
            {
                // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
                Dispose(true);
                // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
                System.GC.SuppressFinalize(this);
            }


            #endregion
        }
        public class WicImageScorce : IImageSource
        {
            ImagingFactory wicFactory;
            WicRenderTarget d2dContext;
            SharpDX.Direct2D1.Factory d2dFactory;
            SharpDX.WIC.Bitmap wicBitmap;
            //System.IO.Stream stream;
            public ImageMaker Parent
            {
                get;set;
            }
            public RenderTarget RenderTarget
            {
                get
                {
                    return d2dContext;
                }
            }
            public WicImageScorce()
            {
                CreateDeviceResources();
            }
            public void CreateDeviceResources()
            {
                wicFactory = new ImagingFactory();
                d2dFactory = new SharpDX.Direct2D1.Factory();



            }
            public Windows.UI.Xaml.Media.Imaging.SoftwareBitmapSource Source { get; set; }= new Windows.UI.Xaml.Media.Imaging.SoftwareBitmapSource();

            Windows.UI.Xaml.Media.ImageSource IImageSource.Source
            {
                get
                {
                    return Source;
                }
            }

            public void BeginDraw()
            {
                // Begin drawing - returns a target surface and an offset to use as the top left origin when drawing.
                try
                {
                    // Begin drawing using D2D context.
                    d2dContext.BeginDraw();

                    // Apply a clip and transform to constrain updates to the target update area.
                    // This is required to ensure coordinates within the target surface remain
                    // consistent by taking into account the offset returned by BeginDraw, and
                    // can also improve performance by optimizing the area that is drawn by D2D.
                    // Apps should always account for the offset output parameter returned by 
                    // BeginDraw, since it may not match the passed updateRect input parameter's location.
                    //d2dContext.PushAxisAlignedClip(
                    //    new RectangleF(
                    //        (0),
                    //        (0),
                    //        (Parent.width),
                    //        (Parent.height)
                    //        ),
                    //    AntialiasMode.Aliased
                    //    );

                    //d2dContext.Transform = Matrix3x2.Translation(0,0);
                }
                catch (SharpDXException ex)
                {
                    if (ex.ResultCode == SharpDX.DXGI.ResultCode.DeviceRemoved ||
                        ex.ResultCode == SharpDX.DXGI.ResultCode.DeviceReset)
                    {
                        // If the device has been removed or reset, attempt to recreate it and continue drawing.
                        CreateDeviceResources();
                        BeginDraw();
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            public async void EndDraw()
            {
                // Remove the transform and clip applied in BeginDraw since
                // the target area can change on every update.
                //d2dContext.Transform = Matrix3x2.Identity;
                //d2dContext.PopAxisAlignedClip();

                // Remove the render target and end drawing.
                d2dContext.EndDraw();

                //stream = new System.IO.MemoryStream();
                //using (var wicstream = new WICStream(wicFactory, stream))
                //{
                //    using (var encoder = new SharpDX.WIC.PngBitmapEncoder(wicFactory))
                //    {
                //        encoder.Initialize(wicstream);

                //        // Frame encoder
                //        using (var bitmapFrameEncode = new BitmapFrameEncode(encoder))
                //        {
                //            bitmapFrameEncode.Initialize();
                //            bitmapFrameEncode.SetSize(Parent.width, Parent.height);
                //            var pixelFormatGuid = SharpDX.WIC.PixelFormat.FormatDontCare;
                //            bitmapFrameEncode.SetPixelFormat(ref pixelFormatGuid);
                //            bitmapFrameEncode.WriteSource(wicBitmap);

                //            bitmapFrameEncode.Commit();
                //            encoder.Commit();

                //            stream.Seek(0, System.IO.SeekOrigin.Begin);
                //        }

                //    }
                //}
                unsafe
                {
                    using (var sbitbuffer = sbitmap.LockBuffer(Windows.Graphics.Imaging.BitmapBufferAccessMode.Write))
                    {
                        using (var reference = sbitbuffer.CreateReference())
                        {
                            byte* dataInBytes;
                            uint capacity;
                            ((Yinyue200.COMInterop.IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacity);

                            // Fill-in the BGRA plane
                            var bufferLayout = sbitbuffer.GetPlaneDescription(0);
                            var intptr = new IntPtr(dataInBytes);
                            wicBitmap.CopyPixels(bufferLayout.Stride, intptr, (int)capacity);
                            //for (int i = 0; i < bufferLayout.Height; i++)
                            //{
                            //    for (int j = 0; j < bufferLayout.Width; j++)
                            //    {
                            //        byte value = (byte)((float)j / bufferLayout.Width * 255);
                            //        dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] = value;
                            //        dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] = value;
                            //        dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] = value;
                            //        dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] = (byte)255;
                            //    }
                            //}
                        }
                    }
                }
                //byte[] buffer = new byte[32 * wicBitmap.Size.Height * wicBitmap.Size.Width];


                await Source.SetBitmapAsync(sbitmap);

            }

            #region IDisposable Support
            private bool disposedValue = false; // 要检测冗余调用

            protected virtual void Dispose(bool disposing)
            {
                if (!disposedValue)
                {
                    if (disposing)
                    {
                        // TODO: 释放托管状态(托管对象)。
                    }

                    Utilities.Dispose(ref wicFactory);
                    Utilities.Dispose(ref d2dContext);
                    Utilities.Dispose(ref d2dFactory);
                    Utilities.Dispose(ref wicBitmap);
                    //stream?.Dispose();
                    sbitmap.Dispose();
                    //Source?.Dispose();


                    disposedValue = true;
                }
            }

            // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
             ~WicImageScorce() {
               // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
               Dispose(false);
             }

            // 添加此代码以正确实现可处置模式。
            public void Dispose()
            {
                // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
                Dispose(true);
                // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
                GC.SuppressFinalize(this);
            }






            #endregion

            Windows.Graphics.Imaging.SoftwareBitmap sbitmap;
            public void SetedParent()
            {
                //有关像素格式的更多信息，请参阅https://msdn.microsoft.com/en-us/library/windows/desktop/dd756766(v=vs.85).aspx
                //Supported WIC Formats
                //When you use the CreateBitmapFromWicBitmap method to create a bitmap from a WIC bitmap, or when you use the CreateSharedBitmap method with an IWICBitmapLock, the WIC source must be in a format supported by Direct2D.
                //WIC format  Corresponding DXGI format Corresponding alpha mode
                //GUID_WICPixelFormat8bppAlpha DXGI_FORMAT_A8_UNORM    D2D1_ALPHA_MODE_STRAIGHT or D2D1_ALPHA_MODE_PREMULTIPLIED
                //GUID_WICPixelFormat32bppPRGBA   DXGI_FORMAT_R8G8B8A8_UNORM D2D1_ALPHA_MODE_PREMULTIPLIED or D2D1_ALPHA_MODE_IGNORE
                //GUID_WICPixelFormat32bppBGR DXGI_FORMAT_B8G8R8A8_UNORM  D2D1_ALPHA_MODE_IGNORE
                //GUID_WICPixelFormat32bppPBGRA   DXGI_FORMAT_B8G8R8A8_UNORM D2D1_ALPHA_MODE_PREMULTIPLIED
                wicBitmap = new SharpDX.WIC.Bitmap(wicFactory, Parent.Width, Parent.Height, SharpDX.WIC.PixelFormat.Format32bppPBGRA, BitmapCreateCacheOption.CacheOnLoad);
                var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new SharpDX.Direct2D1.PixelFormat(Format.B8G8R8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied), 0, 0, RenderTargetUsage.None, SharpDX.Direct2D1.FeatureLevel.Level_DEFAULT);
                d2dContext = new WicRenderTarget(d2dFactory, wicBitmap, renderTargetProperties);
                sbitmap = new Windows.Graphics.Imaging.SoftwareBitmap(Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8, Parent.Width, Parent.Height, Windows.Graphics.Imaging.BitmapAlphaMode.Premultiplied);
                //var disinfo=Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
                //sbitmap.DpiX = disinfo.LogicalDpi;
                //sbitmap.DpiY = disinfo.LogicalDpi;
            }

        }
        public IImageSource TextBlockImageSource { get; set; }

        public SharpDX.DirectWrite.Factory DirectWriteFactory { get; set; }
        public Windows.UI.Xaml.Media.ImageSource Source { get { return TextBlockImageSource.Source; } }
        //private Device d3dDevice;
        public readonly int Width;
        public readonly int Height;
        public ImageMaker(int pixelWidth, int pixelHeight, bool isOpaque,bool enableDxgi)
        {
            Width = pixelWidth;
            Height = pixelHeight;

            DirectWriteFactory = new SharpDX.DirectWrite.Factory();
            if (Device.IsSupportedFeatureLevel(FeatureLevel.Level_9_1)&&enableDxgi)
            {
                TextBlockImageSource = new DxgiImageSource(pixelWidth, pixelHeight, isOpaque) { Parent =this};
            }
            else
            {
                TextBlockImageSource = new WicImageScorce() { Parent = this };
            }
            TextBlockImageSource.SetedParent();

        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                }
                TextBlockImageSource.Dispose();
                DirectWriteFactory.Dispose();

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        ~ImageMaker() {
           // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
           Dispose(false);
         }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            System.GC.SuppressFinalize(this);
        }
        #endregion
    }

}
